home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C/C++ Interactive Reference Guide
/
C-C++ Interactive Reference Guide.iso
/
c_ref
/
csource4
/
289_01
/
othdbm.c
< prev
next >
Wrap
Text File
|
1989-05-25
|
19KB
|
637 lines
/*-----------------------------------------------------------------------------
OTHDBM.C
Revision History
----------------
Jon Ward 17 Oct 1988 Initial Revision.
Jon Ward 30 Oct 1988 Mods for move type.
Jon Ward 30 Oct 1988 Optimized and combined the allocate functions.
Now we use the macros ALLOCATE_LIMB and
ALLOCATE_BOARD.
Jon Ward 04 Dec 1988 Debugging commencing.
Jon Ward 03 Jan 1989 Added relinking ability to db_delete_subtree
changed free_limb from static to global.
-----------------------------------------------------------------------------*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include "othello.h"
/*-----------------------------------------------------------------------------
How This Thing Works:
There will be two data spaces. One for limbs, and one for boards. Each data
space will be maintained as an array. Each array will be bit-mapped so that
we can determine if an element in the array is unused.
-----------------------------------------------------------------------------*/
/*------------------------------------------------
The following structure bd_row_st is used as a
trick to get the MSC compiler to perform a struct
assignment for the board row. The reason for this
is that the database boards are stored in a far
area of ram. Segmentation...Argh!!!!
The dbm_board_st struct is a compressed form of
the board_st structure used every else in the
program. The boards, when compressed, are much
smaller, about 52% smaller or 66% as big as they
would have been.
------------------------------------------------*/
struct bd_row_st
{
unsigned char row [8];
};
struct dbm_board_st
{
struct bd_row_st board [8];
unsigned char fa_bits [FA_BITS_SIZE];
};
typedef struct dbm_board_st dbm_board_type;
#define MAX_DBM_LIMBS 65536 / sizeof (limb_type)
#define MAX_DBM_BOARDS 32768
STATIC limb_type far limb_array [MAX_DBM_LIMBS];
STATIC key_type num_limbs_left; /* number of limbs available */
STATIC key_type num_boards_left; /* number of boards available */
#define MAX_BD_PTRS 50
#define STORED_BOARD_SIZE sizeof (dbm_board_type)
STATIC key_type bd_per_blk = 16384 / STORED_BOARD_SIZE;
STATIC dbm_board_type far *board_ptrs [MAX_BD_PTRS];
STATIC int bd_blks;
typedef unsigned int am_type; /* availability map type */
#define AM_SIZE (8 * sizeof (am_type)) /* bits in availability map type */
STATIC am_type lam [(MAX_DBM_LIMBS + 7) / 8]; /* limb availability map */
STATIC am_type bam [(MAX_DBM_BOARDS + 7) / 8]; /* board availability map */
#define AM_USED ((am_type) (-1))
#define LAM_SIZE (sizeof (lam) / sizeof (lam [0]))
#define BAM_SIZE (sizeof (bam) / sizeof (bam [0]))
/*-----------------------------------------------
The following macros are used to GET, SET, and
CLR bits in the limb and board availability maps.
-----------------------------------------------*/
#define GET_BIT(x,y) (((x) [(y) / AM_SIZE]) & (1 << ((y) % AM_SIZE)))
#define SET_BIT(x,y) (((x) [(y) / AM_SIZE]) |= (1 << ((y) % AM_SIZE)))
#define CLR_BIT(x,y) (((x) [(y) / AM_SIZE]) &= ~(1 << ((y) % AM_SIZE)))
key_type max_limb_key = MAX_DBM_LIMBS; /* maximum key number for limbs */
key_type max_board_key = MAX_DBM_BOARDS; /* maximum key number for boards */
key_type last_parent_key; /* last parent child was added to */
key_type last_child_key; /* last child added */
/*-----------------------------------------------------------------------------
Local Function Prototypes
-----------------------------------------------------------------------------*/
STATIC key_type allocate_limb (int am_sel);
STATIC void free_board (key_type board);
STATIC void free_subtree (key_type key);
STATIC void get_board (
key_type key,
board_type *board);
STATIC void put_board (
key_type key,
board_type *board);
/*-----------------------------------------------------------------------------
The following data is used by the allocate_am function when searching for and
allocating a limb or board element in the database.
-----------------------------------------------------------------------------*/
struct am_struct
{
am_type *am; /* pointer to availability map */
key_type size; /* number of elements in availability map */
key_type *max; /* maximum key for am */
int *num_avail; /* number of available elements */
};
STATIC struct am_struct am_data [] =
{
{ lam, LAM_SIZE, &max_limb_key, &num_limbs_left },
{ bam, BAM_SIZE, &max_board_key, &num_boards_left },
};
#define ALLOCATE_LIMB() (allocate_am (0))
#define ALLOCATE_BOARD() (allocate_am (1))
/*-----------------------------------------------------------------------------
STATIC key_type allocate_am (int am_sel);
This function will locate the next available element in either the limb or
board array. The am_sel argument determines whether the limb (0) or board (1)
map will be used. If a free element if found, it is marked as used, and the
key for referencing that element is returned. If no free space is available,
the NO_KEY manifest constant is returned to indicate an allocation failure.
-----------------------------------------------------------------------------*/
STATIC key_type allocate_am (int am_sel) /* availability map selector */
{
struct am_struct *amp; /* pointer into availability map structure */
register am_type *pt; /* pointer into the limb availability map */
register key_type key; /* key var */
amp = &am_data [am_sel]; /* init pointer to am struct */
for (pt = amp->am; pt < (amp->am + amp->size); pt++)
{
if (*pt != AM_USED) /* if a limb is free */
{
am_type am_entry; /* var copy of am entry */
key = AM_SIZE * (pt - amp->am);
am_entry = *pt;
for (; am_entry & 1; am_entry >>= 1, key++); /* generate the key */
if (key >= *(amp->max)) /* range check the key */
break; /* break if it's too big */
SET_BIT (amp->am, key); /* set alloc bit if range ok */
*(amp->num_avail) -= 1; /* decrement number available */
return (key); /* return the key */
}
}
return (NO_KEY); /* out of limb space */
}
/*-----------------------------------------------------------------------------
void free_limb (key_type limb);
This function will free the array element associated with a previously
allocated limb. The limb argument id the index for the array element to free.
The limb is freed merely by clearing the corresponding bit in the limb
availability map.
-----------------------------------------------------------------------------*/
void free_limb (key_type limb)
{
if (limb >= 0 && limb < max_limb_key && GET_BIT (lam, limb))
{
num_limbs_left++;
CLR_BIT (lam, limb);
}
}
/*-----------------------------------------------------------------------------
STATIC void free_board (key_type board)
This function will free the array element associated with a previously
allocated board. The board argument id the index for the array element to free.
The board is freed merely by clearing the corresponding bit in the board
availability map.
-----------------------------------------------------------------------------*/
STATIC void free_board (key_type board)
{
if (board >= 0 && board < max_board_key && GET_BIT (bam, board))
{
num_boards_left++;
CLR_BIT (bam, board);
}
}
/*-----------------------------------------------------------------------------
void db_init (void);
This function will clear the othello database manager variables and prepare
some data space for the othello program to use. It should only be called once
to initialize the database.
Return Value
None.
-----------------------------------------------------------------------------*/
/*-----------------------------------------------
The following pragma declares the memset function
to generate inline code
-----------------------------------------------*/
#pragma intrinsic (memset)
void db_init ()
{
key_type blk_size; /* block size for allocation */
key_type max_boards;
memset (lam, 0, LAM_SIZE); /* clear limb availability map */
memset (bam, 0, BAM_SIZE); /* clear board availability map */
last_parent_key = NO_KEY;
last_child_key = NO_KEY;
blk_size = bd_per_blk * STORED_BOARD_SIZE;
for (bd_blks = 0; bd_blks < MAX_BD_PTRS; bd_blks++)
{
if ((board_ptrs [bd_blks] = _fmalloc (blk_size)) == NULL)
break;
}
max_boards = bd_blks * bd_per_blk;
if (max_boards < max_board_key)
max_board_key = max_boards;
num_limbs_left = max_limb_key; /* set maximum limb var */
num_boards_left = max_board_key; /* set maximum board var */
#if 0
printf ("Maximum boards = %d\n\n", max_board_key); /***/
#endif
}
/*-----------------------------------------------------------------------------
void db_kill (void);
This function frees the memory allocated for the database.
-----------------------------------------------------------------------------*/
void db_kill (void)
{
register int i;
for (i = 0; i < bd_blks; i++)
_ffree (board_ptrs [i]);
}
/*-----------------------------------------------------------------------------
key_type db_add_child (key_type parent_key,
unsigned char move,
board_type *board,
int score)
This function will add a new child board to the look-ahead move tree. The move
argument contains the move required to get to the board. The score argument
contains the numerical value of the board. The parent_key argument indexes the
parent of the new child.
Return Value
The key for the new child leaf, or -1 if there is no more space in the database
for additions.
-----------------------------------------------------------------------------*/
key_type db_add_child (key_type parent_key,
move_type move,
board_type *board,
int score)
{
key_type child_key;
limb_type far *limb;
if ((child_key = ALLOCATE_LIMB ()) == NO_KEY)
{
/*** Handle no more limb space condition ***/
}
/*-----------------------------------------------
Check to see if the parent's child_key is really
a board key. If it is, free the board and point
the parent to its first born child.
-----------------------------------------------*/
if (parent_key != NO_KEY)
{
limb = &limb_array [parent_key];
if (limb->move & BOARD_MASK)
{
limb->move &= ~BOARD_MASK;
free_board (limb->bc.child_key);
limb->bc.child_key = child_key;
}
}
/*-----------------------------------------------
Save the board, move, score, and sibling for this
limb.
-----------------------------------------------*/
limb = &limb_array [child_key];
if ((limb->bc.child_key = ALLOCATE_BOARD ()) == NO_KEY)
{
free_limb (child_key);
/*** handle condition of no board space available ***/
}
limb->move = move | BOARD_MASK;
limb->score = score;
limb->sibling_key = NO_KEY;
put_board (limb->bc.child_key, board);
/*-----------------------------------------------
If we are adding children other than the first
to a parent, add these children to the end of the
sibling list.
Update the new last parent and last child vars.
-----------------------------------------------*/
if (parent_key != NO_KEY)
{
if (last_parent_key == parent_key)
{
limb_array [last_child_key].sibling_key = child_key;
}
else if (limb_array [parent_key].bc.child_key != child_key)
{
key_type temp;
temp = limb_array [parent_key].bc.child_key;
while (limb_array [temp].sibling_key != NO_KEY) /* find last child */
temp = limb_array [temp].sibling_key;
limb_array [temp].sibling_key = child_key;
}
last_parent_key = parent_key; /* save current parent */
last_child_key = child_key; /* save current child */
}
return (child_key);
}
/*-----------------------------------------------------------------------------
int db_retrieve_board (key_type key,
board_type *board)
This function will retrieve a board from the board database. The key argument
specifies the index for the board to retrieve. The board argument will be
initialized to reflect the board stored in the database.
Return Value
0 board successfully retrieved
-1 board not allocated, not retrieved
-----------------------------------------------------------------------------*/
int db_retrieve_board
(key_type key, /* index into board database */
board_type *board) /* board to init from database */
{
if (!GET_BIT (bam, key)) /* if the board is not allocated */
return (-1); /* return error code */
get_board (key, board); /* get the board */
return (0); /* return success code */
}
/*-----------------------------------------------------------------------------
limb_type far *db_retrieve_limb (key_type key)
This function will retrieve the limb referenced by the key argument. If the key
is out of range, or if the limb is not marked as allocated a NULL pointer
will be returned. If the limb does exist, a far pointer to the limb structure
will be returned.
-----------------------------------------------------------------------------*/
limb_type far *db_retrieve_limb (key_type key)
{
if (key >= 0 && key < max_limb_key)
if (GET_BIT (lam, key))
return (&limb_array [key]);
return (NULL);
}
/*-----------------------------------------------------------------------------
STATIC void free_subtree (key_type key);
This function is called by the db_delete_subtree function and recursively
calls itself to remove all siblings and children starting with the child
referenced by the key argument.
-----------------------------------------------------------------------------*/
STATIC void free_subtree (register key_type key)
{
limb_type far *pt; /* pointer to the limb */
static key_type sibling; /* copy of the sibling index */
do
{
pt = &limb_array [key];
((pt->move & BOARD_MASK) ? free_board : free_subtree) (pt->bc.child_key);
sibling = pt->sibling_key;
free_limb (key);
key = sibling;
}
while (key != NO_KEY);
}
/*-----------------------------------------------------------------------------
int db_delete_subtree (
key_type parent,
key_type child)
This function will delete the sub-tree referenced by the index stored in the
child argument. If no limb exists at child, nothing will be deleted.
-----------------------------------------------------------------------------*/
int db_delete_subtree (
key_type parent,
register key_type child)
{
limb_type far *ch_pt; /* pointer to the child limb */
limb_type far *l; /* pointer to child in chain */
key_type far *left_link; /* link of previous sibling or parent */
key_type chk; /* current child of parent */
last_parent_key = NO_KEY;
last_child_key = NO_KEY;
/*------------------------------------------------
Attempt to get a pointer to the child and parent
limbs. If either is NULL, return indicating an
error.
------------------------------------------------*/
if ((ch_pt = db_retrieve_limb (child)) == NULL)
return (-1);
if ((l = db_retrieve_limb (parent)) == NULL)
return (-1);
/*------------------------------------------------
Loop thru the children of the parent searching for
the specified child. If found, link either the
parent or the previous sibling to the following
sibling. If no mathing child was found, return
an error.
------------------------------------------------*/
chk = *(left_link = &(l->bc.child_key));
for (; chk != NO_KEY; chk = l->sibling_key)
{
l = db_retrieve_limb (chk);
if (chk == child)
{
*left_link = l->sibling_key;
break;
}
left_link = &(l->sibling_key);
}
if (chk != child)
return (-1);
/*------------------------------------------------
Now, delete the child and his subtree.
------------------------------------------------*/
((ch_pt->move & BOARD_MASK) ? free_board : free_subtree) (ch_pt->bc.child_key);
free_limb (child);
return (0);
}
/*-----------------------------------------------------------------------------
int db_almost_full (void);
This function returns a value of 1 if there are less than 40 boards or limbs
remainaing unmapped in the database. A value of 0 is returned if there are at
least 40 limbs AND boards unmapped.
-----------------------------------------------------------------------------*/
int db_almost_full (void)
{
return (num_limbs_left < 40 || num_boards_left < 40) ? 1 : 0;
}
/*-----------------------------------------------------------------------------
void init_borders (board_type *bd);
Fill the board boarders with the proper values.
-----------------------------------------------------------------------------*/
void init_borders (board_type *bd)
{
register unsigned char *p;
register unsigned char *end;
memset (bd->board [0], OFF_BOARD | BD_AX | FD_AX | H_AX | V_AX, 10);
memset (bd->board [9], OFF_BOARD | BD_AX | FD_AX | H_AX | V_AX, 10);
end = bd->board [9];
for (p = bd->board [1]; p < end; p+=10)
{
p [0] = OFF_BOARD | BD_AX | FD_AX | H_AX | V_AX;
p [9] = OFF_BOARD | BD_AX | FD_AX | H_AX | V_AX;
}
}
/*-----------------------------------------------------------------------------
STATIC void get_board (
key_type key,
board_type *board);
This function gets the board whose key is key from the database and stores the
board data in the structure pointed to by the board argument.
-----------------------------------------------------------------------------*/
STATIC void get_board (
key_type key,
board_type *board)
{
div_t ndx;
dbm_board_type far *dbm_bd;
register int i;
ndx = div (key, bd_per_blk);
dbm_bd = &(board_ptrs [ndx.quot]) [ndx.rem];
for (i=0; i<8; i++)
*((struct bd_row_st *) &(board->board [i+1][1])) = dbm_bd->board [i];
for (i=0; i<FA_BITS_SIZE; i++)
board->fa_bits [i] = dbm_bd->fa_bits [i];
init_borders (board);
}
/*-----------------------------------------------------------------------------
STATIC void put_board (
key_type key,
board_type *board);
This function stores the board pointed to by the board argument into the
database at the location indicated by the key argument.
-----------------------------------------------------------------------------*/
STATIC void put_board (
key_type key,
board_type *board)
{
div_t ndx;
dbm_board_type far *dbm_bd;
register int i;
ndx = div (key, bd_per_blk);
dbm_bd = &(board_ptrs [ndx.quot]) [ndx.rem];
for (i=0; i<8; i++)
dbm_bd->board [i] = *((struct bd_row_st *) &(board->board [i+1][1]));
for (i=0; i<FA_BITS_SIZE; i++)
dbm_bd->fa_bits [i] = board->fa_bits [i];
}
/*-----------------------------------------------------------------------------
-----------------------------------------------------------------------------*/